package me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.outbound.inf.OutboundDao;
import me.zhengjie.minbearwcs.dao.storage.inf.MediumStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.entity.DTO.outbound.wcs.MediumStockUp;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.stockUp.OutboundStockUpRecord;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.stockUp.PalletMediumStorageSearchPo;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.MediumStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.OutboundStorage5Or6FloorLocker;
import me.zhengjie.minbearwcs.utils.IdWorker;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class OutboundStorage5Or6FloorLockerImpl implements OutboundStorage5Or6FloorLocker {

    private final SmallStorageDao smallStorageDao;
    private final OutboundDao outboundDao;
    private final IdWorker idWorker;
    private final MediumStorageDao mediumStorageDao;


    @Override
    public synchronized Tuple<String> lockStorage(String smallStorage, String palletCode) {
        smallStorageDao.updateOutboundFreezeBySmallStorageCode(smallStorage, palletCode);
        return Tuple.tuple(null, true);
    }

    @Override
    public Tuple<MediumStockUp> searchAndLockStockUpMedium(String mediumSotrage, Integer floorNum, String materialCode, Integer needNum, boolean isRest) {
        List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList = outboundDao.selectPalletMediumStorage(mediumSotrage);
        int sum = palletMediumStorageSearchPoList
                .stream()
                .filter(a -> materialCode.equals(a.getMaterialCode()))
                .mapToInt(PalletMediumStorageSearchPo::getMaterialNum)
                .sum();

        // 全部出
        if (needNum >= sum) {
            List<OutboundStockUpRecord> collect = palletMediumStorageSearchPoList
                    .stream()
                    .filter(a -> materialCode.equals(a.getMaterialCode()))
                    .map(a -> new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                            a.getPalletCode(), a.getMaterialCode(), a.getMaterialNum(), floorNum, a.getSmallStorageCode(),
                            null, null, LocalDateTime.now()))
                    .collect(Collectors.toList());

            int rest = needNum - sum;
            for (OutboundStockUpRecord lockPallet : collect) {
                smallStorageDao.updateOutboundFreezeBySmallStorageCode(lockPallet.getStartSmallStorage(), lockPallet.getPalletCode());
            }
            return Tuple.tuple(new MediumStockUp(collect, rest), true);
        }


        // 几个出口
        long enterCount = palletMediumStorageSearchPoList
                .stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        List<OutboundStockUpRecord> resultList = new ArrayList<>();
        Tuple<MediumStockUp> mediumStockUpTuple = Tuple.tuple(null, false);
        if (enterCount == 2) {
            // 多少个批次
            long batchCount = palletMediumStorageSearchPoList
                    .stream()
                    .filter(a-> a.getMaterialCode() != null && a.getBatchCode() != null && a.getInboundOrderCode() != null)
                    .map(a -> a.getMaterialCode() + a.getBatchCode() + a.getInboundOrderCode())
                    .distinct()
                    .count();

            if (batchCount == 1) {
                // 从后往前
                if (isRest) {
                    mediumStockUpTuple = searchBackToFrontRest(palletMediumStorageSearchPoList, floorNum, needNum);
                } else {
                    mediumStockUpTuple = searchBackToFront(palletMediumStorageSearchPoList, floorNum, needNum);
                }
                if (mediumStockUpTuple.success) return mediumStockUpTuple;
            } else if (batchCount == 2) {
                // 判断哪边是入口
                Optional<PalletMediumStorageSearchPo> min = palletMediumStorageSearchPoList
                        .stream()
                        .min(Comparator.comparingInt(PalletMediumStorageSearchPo::getColumnNum));

                Optional<PalletMediumStorageSearchPo> max = palletMediumStorageSearchPoList
                        .stream()
                        .max(Comparator.comparingInt(PalletMediumStorageSearchPo::getColumnNum));

                if (min.isPresent() && max.isPresent()) {
                    PalletMediumStorageSearchPo minTemp = min.get();
                    PalletMediumStorageSearchPo maxTemp = max.get();
                    if (materialCode.equals(minTemp.getMaterialCode()) && materialCode.equals(maxTemp.getMaterialCode())) {
                        if (Duration.between(minTemp.getInboundDate(), maxTemp.getInboundDate()).toMillis() > 0) {
                            // 出小的
                            // 从前往后
                            if (isRest) {
                                mediumStockUpTuple = searchFrontToBackRest(palletMediumStorageSearchPoList, floorNum, needNum);
                            } else {
                                mediumStockUpTuple = searchFrontToBack(palletMediumStorageSearchPoList, floorNum, needNum);
                            }
                            if (mediumStockUpTuple.success) return mediumStockUpTuple;
                        } else {
                            // 出大的
                            // 从后往前
                            if (isRest) {
                                mediumStockUpTuple = searchBackToFrontRest(palletMediumStorageSearchPoList, floorNum, needNum);
                            } else {
                                mediumStockUpTuple = searchBackToFront(palletMediumStorageSearchPoList, floorNum, needNum);
                            }
                            if (mediumStockUpTuple.success) return mediumStockUpTuple;
                        }
                    } else if (minTemp.getMaterialCode().equals(materialCode)) {
                        // 从前往后
                        if (isRest) {
                            mediumStockUpTuple = searchFrontToBackRest(palletMediumStorageSearchPoList, floorNum, needNum);
                        } else {
                            mediumStockUpTuple = searchFrontToBack(palletMediumStorageSearchPoList, floorNum, needNum);
                        }
                        if (mediumStockUpTuple.success) return mediumStockUpTuple;
                    } else if (maxTemp.getMaterialCode().equals(materialCode)) {
                        // 从后往前
                        if (isRest) {
                            mediumStockUpTuple = searchBackToFrontRest(palletMediumStorageSearchPoList, floorNum, needNum);
                        } else {
                            mediumStockUpTuple = searchBackToFront(palletMediumStorageSearchPoList, floorNum, needNum);
                        }
                        if (mediumStockUpTuple.success) return mediumStockUpTuple;
                    }
                }

            }


        } else if (enterCount == 1) {
            PalletMediumStorageSearchPo enter = palletMediumStorageSearchPoList.get(0);
            if (enter.getIsEntrance() == 1) {
                // 从前往后
                if (isRest) {
                    mediumStockUpTuple = searchFrontToBackRest(palletMediumStorageSearchPoList, floorNum, needNum);
                } else {
                    mediumStockUpTuple = searchFrontToBack(palletMediumStorageSearchPoList, floorNum, needNum);
                }

                if (mediumStockUpTuple.success) return mediumStockUpTuple;
            } else {
                // 从后往前
                if (isRest) {
                    mediumStockUpTuple = searchBackToFrontRest(palletMediumStorageSearchPoList, floorNum, needNum);
                } else {
                    mediumStockUpTuple = searchBackToFront(palletMediumStorageSearchPoList, floorNum, needNum);
                }

                if (mediumStockUpTuple.success) return mediumStockUpTuple;
            }
        }


        return Tuple.tuple(null, false);
    }


    // 从前往后
    private Tuple<MediumStockUp> searchFrontToBack(List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList, Integer floorNum, Integer needNum) {
        List<OutboundStockUpRecord> resultList = new ArrayList<>();
        for (int i = 0; i < palletMediumStorageSearchPoList.size(); i++) {
            PalletMediumStorageSearchPo tempSingle = palletMediumStorageSearchPoList.get(i);
            if (tempSingle.getPalletCode() == null) continue;

            needNum = needNum - tempSingle.getMaterialNum();
            if (needNum < 0 || tempSingle.getOutboundFreezePallet() != null) {
                needNum = needNum + tempSingle.getMaterialNum();
                break;
            }


            resultList.add(
                    new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                            tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(),
                            null, null, LocalDateTime.now())
            );
        }

        for (OutboundStockUpRecord lockPallet : resultList) {
            smallStorageDao.updateOutboundFreezeBySmallStorageCode(lockPallet.getStartSmallStorage(), lockPallet.getPalletCode());
        }
        return Tuple.tuple(new MediumStockUp(resultList, needNum), true);
    }


    // 从后往前
    private Tuple<MediumStockUp> searchBackToFront(List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList, Integer floorNum, Integer needNum) {
        List<OutboundStockUpRecord> resultList = new ArrayList<>();
        for (int i = palletMediumStorageSearchPoList.size() - 1; i >= 0; i--) {
            PalletMediumStorageSearchPo tempSingle = palletMediumStorageSearchPoList.get(i);
            if (tempSingle.getPalletCode() == null) continue;

            needNum = needNum - tempSingle.getMaterialNum();
            if (needNum < 0 || tempSingle.getOutboundFreezePallet() != null) {
                needNum = needNum + tempSingle.getMaterialNum();
                break;
            }

            resultList.add(
                    new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                            tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(),
                            null, null, LocalDateTime.now())
            );
        }

        for (OutboundStockUpRecord lockPallet : resultList) {
            smallStorageDao.updateOutboundFreezeBySmallStorageCode(lockPallet.getStartSmallStorage(), lockPallet.getPalletCode());
        }
        return Tuple.tuple(new MediumStockUp(resultList, needNum), true);
    }


    // 从前往后 , 分拣
    private Tuple<MediumStockUp> searchFrontToBackRest(List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList, Integer floorNum, Integer needNum) {
        List<OutboundStockUpRecord> resultList = new ArrayList<>();
        for (int i = 0; i < palletMediumStorageSearchPoList.size(); i++) {
            PalletMediumStorageSearchPo tempSingle = palletMediumStorageSearchPoList.get(i);
            if (tempSingle.getPalletCode() == null) continue;

            needNum = needNum - tempSingle.getMaterialNum();
            if (tempSingle.getOutboundFreezePallet() != null) {
                needNum = needNum + tempSingle.getMaterialNum();
                break;
            }

            if (needNum < 0) {
                resultList.add(
                        new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                                tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(),
                                null, null, LocalDateTime.now())
                );
                break;
            }


            resultList.add(
                    new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                            tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(), null, null, LocalDateTime.now())
            );
        }

        for (OutboundStockUpRecord lockPallet : resultList) {
            smallStorageDao.updateOutboundFreezeBySmallStorageCode(lockPallet.getStartSmallStorage(), lockPallet.getPalletCode());
        }
        return Tuple.tuple(new MediumStockUp(resultList, needNum), true);
    }


    // 从后往前 , 分拣
    private Tuple<MediumStockUp> searchBackToFrontRest(List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList, Integer floorNum, Integer needNum) {
        List<OutboundStockUpRecord> resultList = new ArrayList<>();
        for (int i = palletMediumStorageSearchPoList.size() - 1; i >= 0; i--) {
            PalletMediumStorageSearchPo tempSingle = palletMediumStorageSearchPoList.get(i);
            if (tempSingle.getPalletCode() == null) continue;

            needNum = needNum - tempSingle.getMaterialNum();
            if (tempSingle.getOutboundFreezePallet() != null) {
                needNum = needNum + tempSingle.getMaterialNum();
                break;
            }

            if (needNum < 0) {
                resultList.add(
                        new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                                tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(), null, null, LocalDateTime.now())
                );
                break;
            }

            resultList.add(
                    new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                            tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(), null, null, LocalDateTime.now())
            );
        }

        for (OutboundStockUpRecord lockPallet : resultList) {
            smallStorageDao.updateOutboundFreezeBySmallStorageCode(lockPallet.getStartSmallStorage(), lockPallet.getPalletCode());
        }
        return Tuple.tuple(new MediumStockUp(resultList, needNum), true);
    }


    @Override
    public Tuple<OutboundStockUpRecord> lockDestinationNullInMedium(String mediumSotrage, Integer targetFloorNum, OutboundStockUpRecord outboundStockUpRecord) {
        List<SmallStorage> smallStorageList = smallStorageDao.selectSmallStorageByMediunStorage(mediumSotrage);


        // 判断几个入口
        long enterCount = smallStorageList.stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        if (enterCount == 2) {
            Optional<SmallStorage> min = smallStorageList.stream()
                    .filter(a -> a.getPalletCode() == null &&
                            a.getInboundFreezePallet() == null &&
                            a.getOutboundFreezePallet() == null)
                    .min(Comparator.comparingInt(SmallStorage::getColumnNum));

            if (min.isPresent()) {
                SmallStorage lockStorage = min.get();
                smallStorageDao.updateInboundFreezePallet(lockStorage.getId(), outboundStockUpRecord.getPalletCode());
                outboundStockUpRecord.setDestinationFloorNum(targetFloorNum);
                outboundStockUpRecord.setDestinationSmallStorage(lockStorage.getSmallStorageCode());
                updateMediumStorageOutboundOrder(mediumSotrage, outboundStockUpRecord.getOutboundOrderCode());
                return Tuple.tuple(outboundStockUpRecord, true);
            }

        } else if (enterCount == 1) {
            SmallStorage firstStorage = smallStorageList.get(0);
            if (firstStorage.getIsEntrance() == 1) {
                for (int i = smallStorageList.size() - 1; i >= 0; i--) {
                    SmallStorage tempStorage = smallStorageList.get(i);

                    if (tempStorage.getPalletCode() == null &&
                            tempStorage.getInboundFreezePallet() == null &&
                            tempStorage.getOutboundFreezePallet() == null) {
                        smallStorageDao.updateInboundFreezePallet(tempStorage.getId(), outboundStockUpRecord.getPalletCode());
                        outboundStockUpRecord.setDestinationFloorNum(targetFloorNum);
                        outboundStockUpRecord.setDestinationSmallStorage(tempStorage.getSmallStorageCode());
                        updateMediumStorageOutboundOrder(mediumSotrage, outboundStockUpRecord.getOutboundOrderCode());
                        return Tuple.tuple(outboundStockUpRecord, true);
                    }

                }
            } else {
                for (int i = 0; i < smallStorageList.size(); i++) {
                    SmallStorage tempStorage = smallStorageList.get(i);

                    if (tempStorage.getPalletCode() == null &&
                            tempStorage.getInboundFreezePallet() == null &&
                            tempStorage.getOutboundFreezePallet() == null) {
                        smallStorageDao.updateInboundFreezePallet(tempStorage.getId(), outboundStockUpRecord.getPalletCode());
                        outboundStockUpRecord.setDestinationFloorNum(targetFloorNum);
                        outboundStockUpRecord.setDestinationSmallStorage(tempStorage.getSmallStorageCode());
                        updateMediumStorageOutboundOrder(mediumSotrage, outboundStockUpRecord.getOutboundOrderCode());
                        return Tuple.tuple(outboundStockUpRecord, true);
                    }
                }
            }
        }

        return Tuple.tuple(outboundStockUpRecord, false);
    }


    private void updateMediumStorageOutboundOrder(String mediumSotrage, String outboundOrder) {
        MediumStorage mediumStorage = mediumStorageDao.selectByMediumStorage(mediumSotrage);
        if (mediumStorage.getOutboundOrder() == null) {
            mediumStorageDao.updateOutboundOrderByMediumStorage(mediumSotrage, outboundOrder);
        }
    }


    @Override
    public Tuple<String> lockDestinationStorage(String smallStoargeCode, String palletCode) {
        smallStorageDao.updateInboundFreezePallet(smallStoargeCode, palletCode);
        return Tuple.tuple(null, true);
    }

    @Override
    public Tuple<OutboundStockUpRecord> searchAndLockSortMedium(String mediumSotrage, Integer floorNum, String materialCode, Integer needNum) {
        List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList = outboundDao.selectPalletMediumStorage(mediumSotrage);


        // 几个出口
        long enterCount = palletMediumStorageSearchPoList
                .stream()
                .filter(a -> a.getIsEntrance() == 1)
                .count();

        if (enterCount == 2) {
            // 多少个批次
            long batchCount = palletMediumStorageSearchPoList
                    .stream()
                    .filter(a-> a.getMaterialCode() != null && a.getBatchCode() != null && a.getInboundOrderCode() != null)
                    .map(a -> a.getMaterialCode() + a.getBatchCode() + a.getInboundOrderCode())
                    .distinct()
                    .count();

            if (batchCount == 1) {
                // 从后往前
                return searchBackToFrontForSort(palletMediumStorageSearchPoList, floorNum, needNum);
            } else if (batchCount == 2) {
                // 判断哪边是入口
                Optional<PalletMediumStorageSearchPo> min = palletMediumStorageSearchPoList
                        .stream()
                        .filter(a->a.getPalletCode() != null)
                        .min(Comparator.comparingInt(PalletMediumStorageSearchPo::getColumnNum));

                Optional<PalletMediumStorageSearchPo> max = palletMediumStorageSearchPoList
                        .stream()
                        .filter(a->a.getPalletCode() != null)
                        .max(Comparator.comparingInt(PalletMediumStorageSearchPo::getColumnNum));

                if (min.isPresent() && max.isPresent()) {
                    PalletMediumStorageSearchPo minTemp = min.get();
                    PalletMediumStorageSearchPo maxTemp = max.get();
                    if (materialCode.equals(minTemp.getMaterialCode()) && materialCode.equals(maxTemp.getMaterialCode())) {
                        if (Duration.between(minTemp.getInboundDate(), maxTemp.getInboundDate()).toMillis() > 0) {
                            // 出小的
                            // 从前往后
                            return searchFrontToBackForSort(palletMediumStorageSearchPoList, floorNum, needNum);
                        } else {
                            // 出大的
                            // 从后往前
                            return searchBackToFrontForSort(palletMediumStorageSearchPoList, floorNum, needNum);
                        }
                    } else if (minTemp.getMaterialCode().equals(materialCode)) {
                        // 从前往后
                        return searchFrontToBackForSort(palletMediumStorageSearchPoList, floorNum, needNum);
                    } else if (maxTemp.getMaterialCode().equals(materialCode)) {
                        // 从后往前
                        return searchBackToFrontForSort(palletMediumStorageSearchPoList, floorNum, needNum);
                    }
                }

            }


        } else if (enterCount == 1) {
            List<PalletMediumStorageSearchPo> collect = palletMediumStorageSearchPoList
                    .stream()
                    .filter(a -> a.getIsEntrance() == 1)
                    .collect(Collectors.toList());

            PalletMediumStorageSearchPo enter = collect.get(0);
            if (enter.getColumnNum() == 0) {
                // 从前往后
                return searchFrontToBackForSort(palletMediumStorageSearchPoList, floorNum, needNum);
            } else {
                // 从后往前
                return searchBackToFrontForSort(palletMediumStorageSearchPoList, floorNum, needNum);
            }
        }


        return Tuple.tuple(null, false);
    }


    // 从前往后 , 分拣
    private Tuple<OutboundStockUpRecord> searchFrontToBackForSort(List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList, Integer floorNum, Integer needNum) {
        for (int i = 0; i < palletMediumStorageSearchPoList.size(); i++) {
            PalletMediumStorageSearchPo tempSingle = palletMediumStorageSearchPoList.get(i);
            if (tempSingle.getPalletCode() == null) continue;
            if (tempSingle.getOutboundFreezePallet() != null) return Tuple.tuple(null, false);

            needNum = needNum - tempSingle.getMaterialNum();

            if (needNum <= 0) {
                OutboundStockUpRecord outboundStockUpRecord = new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                        tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(),
                        null, null, LocalDateTime.now());
                smallStorageDao.updateOutboundFreezeBySmallStorageCode(
                        tempSingle.getSmallStorageCode(),
                        tempSingle.getPalletCode()
                );
                return Tuple.tuple(outboundStockUpRecord, true);
            } else {
                return Tuple.tuple(null, false);
            }

        }

        return Tuple.tuple(null, false);
    }


    // 从后往前 , 分拣
    private Tuple<OutboundStockUpRecord> searchBackToFrontForSort(List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList, Integer floorNum, Integer needNum) {
        for (int i = palletMediumStorageSearchPoList.size() - 1; i >= 0; i--) {
            PalletMediumStorageSearchPo tempSingle = palletMediumStorageSearchPoList.get(i);
            if (tempSingle.getPalletCode() == null) continue;
            if (tempSingle.getOutboundFreezePallet() != null) return Tuple.tuple(null, false);

            needNum = needNum - tempSingle.getMaterialNum();

            if (needNum <= 0) {
                OutboundStockUpRecord outboundStockUpRecord = new OutboundStockUpRecord(null, "stock_up_" + idWorker.nextId(), null, null,
                        tempSingle.getPalletCode(), tempSingle.getMaterialCode(), tempSingle.getMaterialNum(), floorNum, tempSingle.getSmallStorageCode(),
                        null, null, LocalDateTime.now());
                smallStorageDao.updateOutboundFreezeBySmallStorageCode(
                        tempSingle.getSmallStorageCode(),
                        tempSingle.getPalletCode()
                );
                return Tuple.tuple(outboundStockUpRecord, true);
            } else {
                return Tuple.tuple(null, false);
            }

        }

        return Tuple.tuple(null, false);
    }


}
